#include <string.h>
#include <sys/time.h>
#include <sys/epoll.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>

#include "Log.h"
#include "Public.h"
#include "Socket.h"
#include "Connect.h"


namespace sevent{

Address::Address(){
	memset(&_addr, 0, sizeof(_addr));
}

Address::Address(const std::string ip, const int port){
	memset(&_addr, 0, sizeof(_addr));
	_addr.sin_family = AF_INET;
	_addr.sin_port = htons(port);
	int ret = inet_pton(AF_INET, ip.c_str(), &_addr.sin_addr);
	if (ret != 1) {
		LOG_ERROR_VA("inet_pton failed to convert ip addr.");
	}
}

Address::Address(const Address &other):_addr(other._addr){
}

Address::~Address(){
}

int Address::setAddress(const std::string ip, const int port){
	int ret = 0;
	memset(&_addr, 0, sizeof(_addr));
	_addr.sin_family = AF_INET;
	_addr.sin_port = htons(port);
	ret = inet_pton(AF_INET, ip.c_str(), &_addr.sin_addr);
	if (ret != 1) {
		LOG_ERROR_VA("inet_pton failed to convert ip addr.");
	}
	return ret;
}

int Address::port(){
	return ntohs(_addr.sin_port);
}

std::string Address::ip(){
	char ip[IPADDRESSMAXLEN];
	const char *ret = inet_ntop(AF_INET, &(_addr.sin_addr), ip, IPADDRESSMAXLEN);
	if (NULL == ret) {
		LOG_ERROR_VA("convert inet_ntop error.");
	}
	return std::string(ip);
}


int Poll(int fd, short events, int timeout) {
	struct pollfd pfd;
	memset(&pfd, 0, sizeof(pfd));
	pfd.fd = fd;
	pfd.events |= events;
	int ret = 0;
	while (1) {
		ret = poll(&pfd, 1, timeout);
		if (ret < 0) {
			if (errno == EINTR) {
				continue;
			}
			LOG_ERROR_VA("poll error %s",strerror(errno));
		} else if (0 == ret) {
			LOG_ERROR_VA("poll timeout %s", strerror(errno));
		}
		return ret;
	}
}

int Read(int fd, void *buf, size_t len, int timeout) {
	int ret = Poll(fd, POLLIN, timeout);
	if (ret > 0) {
		ret = ::read(fd, buf, len);
		return ret;
	} else if (ret == 0) {
		LOG_ERROR_VA("read time out");
		return -2;
		//timeout
	} else {
		return -3;
	}
}

int Write(int fd, void *buf, size_t len, int timeout) {
	int ret = Poll(fd, POLLOUT, timeout);
	if (ret > 0) {
		ret = ::write(fd, buf, len);
		return ret;
	} else if (ret == 0) {
		LOG_ERROR_VA("write time out");
		return -2;
	} else {
		return -3;
	}
}

int tcpRead(int fd, void *buf, int len, int timeout) {
	ssize_t nread;
	size_t nleft = len;
	char *ptr = (char *)buf;

	//we need block mode for timeout
	int sockflag = fcntl(fd, F_GETFL, 0);
	if (sockflag < 0) {
		LOG_ERROR_VA("fcntl error %s", strerror(errno));
		return -1;
	}
	if (!(sockflag & O_NONBLOCK)) {
		if (fcntl(fd, F_SETFL, sockflag | O_NONBLOCK) < 0) {
			LOG_ERROR_VA("fcntl error %s", strerror(errno));
			return -1;
		}
	}

	while (nleft > 0) {
		nread = Read(fd, ptr, nleft, timeout);
		if (nread < 0) {
			if ((nread == -1) && (errno == EINTR)) {
				LOG_WARN_VA("read intered by signal %s", strerror(errno));
				continue;
			}
			if ((nread == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)) {
				LOG_WARN_VA("how could this happen. Poll ready but read return EAGAIN %s", strerror(errno));
				continue;
			}
			if (nread == -2) {
				LOG_ERROR_VA("read timeout %s", strerror(errno));
			} else if (nread == -3) {
				LOG_ERROR_VA("read error %s", strerror(errno));
			}
			if (!(sockflag & O_NONBLOCK)) {
				if (fcntl(fd, F_SETFL, sockflag) < 0) {
					LOG_ERROR_VA("fcntl error %s", strerror(errno));
					return -1;
				}
			}
			return -1;
		} else if (nread == 0) {
			//read the end of fd
			break;
		} else {
			ptr += nread;
			nleft -= nread;
		}
	}
	if (!(sockflag & O_NONBLOCK)) {
		if (fcntl(fd, F_SETFL, sockflag) < 0) {
			LOG_ERROR_VA("fcntl error %s", strerror(errno));
			return -1;
		}
	}
	return (ssize_t) (len - nleft);
}

int tcpWrite(int fd, void *buf, int len, int timeout) {
	ssize_t nwrite;
	size_t nleft = len;
	char *ptr = (char *)buf;

	int sockflag = fcntl(fd, F_GETFL, 0);
	if (sockflag < 0) {
		LOG_ERROR_VA("fcntl error %s", strerror(errno));
		return -1;
	}
	if (!(sockflag & O_NONBLOCK)) {
		if (fcntl(fd, F_SETFL, sockflag | O_NONBLOCK) < 0) {
			LOG_ERROR_VA("fcntl error %s", strerror(errno));
			return -1;
		}
	}

	while (nleft > 0) {
		nwrite = Write(fd, ptr, nleft, timeout);
		if (nwrite < 0) {
			if (nwrite == -1 && errno == EINTR) {
				LOG_WARN_VA("write intered by signal.Continue.");
				continue;
			}
			if ((nwrite == -1) && (errno == EAGAIN || errno == EWOULDBLOCK)) {
				LOG_WARN_VA("how could this happen. >oll ready but write return EAGAIN: %s", strerror(errno));
				continue;
			}
			if (nwrite == -2) {
				LOG_ERROR_VA("write timeout: %s", strerror(errno));
			} else if (nwrite == -3) {
				LOG_ERROR_VA("write error: %s", strerror(errno));
			}
			if (!(sockflag & O_NONBLOCK)) {
				if (fcntl(fd, F_SETFL, sockflag) < 0) {
					LOG_ERROR_VA("fcntl error: %s", strerror(errno));
					return -1;
				}
			}
			return -1;
		} else if (nwrite == 0) {
			break;
		}
		nleft -= nwrite;
		ptr += nwrite;
	}
	if (!(sockflag & O_NONBLOCK)) {
		if (fcntl(fd, F_SETFL, sockflag) < 0) {
			LOG_ERROR_VA("fcntl error: %s", strerror(errno));
			return -1;
		}
	}
	return (ssize_t) (len - nleft);
}

int tcpConnect(Address &peerAddr){
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd < 0) {
		LOG_ERROR_VA("create socket error");
		return -1;
	}
	int flag = 1;
	setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int));
	struct timeval connTimeout;
	connTimeout.tv_sec = 0;
	connTimeout.tv_usec = 100000; //100ms
	setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&connTimeout, sizeof(connTimeout));
	if(::connect(fd, (struct sockaddr *)&peerAddr.addr(), peerAddr.length()) < 0) {
		LOG_ERROR_VA("socket connect error");
		return -1;
	}
	return fd;
}


//int tcpAccept(int fd, std::vector<int> &fdVec, std::vector<Address> &addrVec) {
int tcpAccept(int fd, std::vector<Connect *> &connectVec) {
	int taskfd = -1;
	struct sockaddr_in peerAddr;
	socklen_t peerAddrLen = sizeof(peerAddr);
	while(1){
		taskfd = ::accept(fd, (struct sockaddr *)&peerAddr, &peerAddrLen);
		if(taskfd < 0){
			if (errno == EAGAIN || errno == EWOULDBLOCK) {
				break;
			}else if (errno == EINTR) {
				continue;
			}else{
				char errbuf[1024] = {0};
				strerror_r(errno, errbuf, 1023);
				LOG_ERROR_VA("socket accept error: %s", errbuf);
				return -1;
			}
		}else{
			Address addr(inet_ntoa(peerAddr.sin_addr), ntohs(peerAddr.sin_port));
			Connect *co = new Connect(taskfd, addr);
			connectVec.push_back(co);
			LOG_DEBUG_VA("add Connect %x with fd %d ", co, taskfd);
		}
	}
	return 0;
}

int tcpEpollWait(int fd, struct epoll_event *events, int maxevent, int timeout) {
	int numfd = 0;
	while (1) {
		numfd = epoll_wait(fd, events, maxevent, timeout);
		if(numfd > 0){
			return numfd;
		}else if(numfd < 0 && errno == EINTR){
			continue;
		}else{
			char errbuf[1024];
			strerror_r(errno, errbuf, 1023);
			LOG_WARN_VA("epoll wait error: %s", errbuf);
			return numfd;
		}
	}
}

int tcpEpollAdd(int epfd, int fd, void *ptr, int events) {
	int ret = 0;
	struct epoll_event ev;
//	ev.data.ptr = ptr;
	ev.data.u64 = 0UL;
	ev.data.fd = fd;
	ev.events = events;
	if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
		char errbuf[1024];
		strerror_r(errno, errbuf, 1023);
		LOG_ERROR_VA("socket %d cannot be added to epoll %d : %s", fd, epfd, errbuf);
		return -1;
	}
	LOG_DEBUG_VA("socket %d added to epoll %d", fd, epfd);
	return ret;
}

int tcpClose(int fd) {
	int ret = 0;
	if(fd <= 2){
		LOG_ERROR_VA("socket fd:%d shouldn't be closed", fd);
		return -1;
	}
	if (fd > 3) {
		ret = ::close(fd);
		while ((ret < 0) && (errno == EINTR)) {
			ret = ::close(fd);
		}
	}
	return ret;
}

//TODO:inherit from prev version.
//This should NOT work since it is hard to check the socket status without actually writing a LOT data to it.
int tcpAlive(int fd) {
	if (fd < 0) {
		return 0;
	}
	struct pollfd pfd;
	memset(&pfd, 0, sizeof(pfd));
	pfd.fd = fd;
	pfd.events |= POLLOUT;
	int ret = poll(&pfd, 1, 0);
	if (ret < 0) {
		LOG_ERROR_VA("poll error: %s", strerror(errno));
		return ret;
	}
	if (ret == 1 && (pfd.revents & POLLOUT)) {
		return 1;
	} else {
		return 0;
	}
}



}
